package config

import (
	"deploy-tools/model"
	"deploy-tools/utils"
	"errors"
	"fmt"
	"gopkg.in/yaml.v3"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

type Config struct {
	Localhost   string            `yaml:"localhost"`
	SSHPort     int               `yaml:"ssh_port"`
	SSHUser     string            `yaml:"ssh_user"`
	SSHPassword string            `yaml:"ssh_password"`
	Hosts       []*model.Host     `yaml:"hosts"`
	Jobs        []*model.Job      `yaml:"jobs"`
	EncryptKey  string            `yaml:"encrypt_key"`
	Vars        map[string]string `yaml:"vars"` // 全局的参数
}

var GlobalConfig = &Config{}
var HostMap = make(map[string]*model.Host)
var AllHosts = make([]string, 0)
var InternalVars = make(map[string]string)
var HostGroup = make(map[string][]string)
var GlobalVars = make(map[string]string)
var VarsCache *Cache

func LoadConfig(path string) error {
	absPath, err := filepath.Abs(path)
	if err != nil {
		return err
	}
	log.Printf("Load config file: %s", absPath)
	yamlFile, err := os.ReadFile(absPath)
	if err != nil {
		return err
	}
	utils.TryDir = filepath.Dir(absPath)
	err = yaml.Unmarshal(yamlFile, GlobalConfig)
	if err != nil {
		return err
	}

	availableHost := make([]*model.Host, 0)
	for _, host := range GlobalConfig.Hosts {
		if !host.Disabled {
			availableHost = append(availableHost, host)
		}
	}
	GlobalConfig.Hosts = availableHost

	for _, host := range GlobalConfig.Hosts {
		if host.Port == 0 {
			host.Port = GlobalConfig.SSHPort
		}
		if host.User == "" {
			host.User = GlobalConfig.SSHUser
		}
		if host.Password == "" {
			host.Password = GlobalConfig.SSHPassword
		}
		HostMap[host.Name] = host
		AllHosts = append(AllHosts, host.Name)

		for _, tag := range host.Tags {
			group := HostGroup[tag]
			if group == nil {
				group = make([]string, 0)
			}
			group = append(group, host.Name)
			HostGroup[tag] = group
		}

		InternalVars["HOST_"+host.Name] = host.Host
		InternalVars["SSH_PASS_"+host.Name] = host.Password
		InternalVars["SSH_PORT_"+host.Name] = strconv.Itoa(host.Port)
		InternalVars["SSH_USER_"+host.Name] = host.User
	}
	for k, v := range GlobalConfig.Vars {
		GlobalVars[k] = ReplaceVars(fmt.Sprintf("%s", v), true)
	}
	for k, v := range GlobalConfig.Vars {
		if !strings.Contains(v, "{{") {
			continue
		}
		GlobalVars[k] = ReplaceVars(fmt.Sprintf("%s", v), false)
	}
	VarsCache, err = NewCache("vars")
	if err != nil {
		return err
	}
	for k, v := range VarsCache.Values {
		GlobalVars[k] = v
	}
	return nil
}

func LoadJobs(path string) error {
	if path == "" {
		return nil
	}
	jobPath, err := filepath.Abs(path)
	if err != nil {
		return err
	}
	log.Printf("Loading jobs from %s", jobPath)
	yamlFile, err := os.ReadFile(jobPath)
	if err != nil {
		return err
	}
	var cfg *Config
	err = yaml.Unmarshal(yamlFile, &cfg)
	if err != nil {
		return err
	}
	log.Printf("Loaded %d jobs", len(cfg.Jobs))
	GlobalConfig.Jobs = cfg.Jobs
	return nil
}

func DecryptConfig() error {
	for _, host := range GlobalConfig.Hosts {
		if strings.HasPrefix(host.Password, "ENC(") {
			cipherText := host.Password[4 : len(host.Password)-1]
			if GlobalConfig.EncryptKey == "" {
				return errors.New("请配置加密秘钥或者指定参数--key")
			}
			password, err := utils.Decrypt(cipherText, GlobalConfig.EncryptKey)
			if err != nil {
				return err
			}
			host.Password = password
		}
	}
	return nil
}

// CacheVar 设置并缓存全局变量
func CacheVar(key string, val string) {
	GlobalVars[key] = VarsCache.Cache(key, val)
}

// ReplaceVars 参数替换， internal，仅内置参数
func ReplaceVars(source string, internal bool) string {
	target := source
	for k, v := range InternalVars {
		target = strings.ReplaceAll(target, fmt.Sprintf("{{%s}}", k), v)
	}
	if internal {
		return target
	}
	for k, v := range GlobalVars {
		target = strings.ReplaceAll(target, fmt.Sprintf("{{%s}}", k), v)
	}
	return target
}
